#pragma once

#include "CogProcessors.h"
#include "CogEvaluator.hpp"

// ------------------------------------------------------------------------------------------

template <class TD>
CogArithmeticProcessor<TD>::CogArithmeticProcessor()
: maxVariables(0)
{
	validOps[OP_VAR] = CogOpInfo("var", 0);
	validOps[OP_CONST] = CogOpInfo("const",  0);
	validOps[OP_ADD] = CogOpInfo("add", 100);
	validOps[OP_SUB] = CogOpInfo("sub", 100);
	validOps[OP_MUL] = CogOpInfo("mul", 200);
	validOps[OP_DIV] = CogOpInfo("div", 200);
	validOps[OP_NEG] = CogOpInfo("neg", 300);
}

template <class TD>
bool CogArithmeticProcessor<TD>::readParameter(size_t opId, const string& value)
{
	if ((opId == OP_VAR) || (opId == OP_CONST))
	{
		double dvalue = atof(value.c_str());
		if (!dvalue)
		{
			for (size_t i = 0; i < value.size(); i++)
			{
				if ((value[i] != '0') && (value[i] != '.'))
				{
					return false;
				}
			}
		}
		if (opId == OP_VAR)
		{
			if ((dvalue < 0) || (static_cast<size_t>(dvalue) >= maxVariables))
			{
				return false;
			}
			parameters.push_back(CogParameterizedOp(true, static_cast<size_t>(dvalue)));
		}
		else
		{
			parameters.push_back(CogParameterizedOp(false, constants.size()));
			constants.push_back(static_cast<TD>(dvalue));
		}
		return true;
	}
	return false;
}

template <class TD>
inline
TD CogArithmeticProcessor<TD>::processOperator(size_t id, stack<TD>& result)
{
	switch (id)
	{
	case OP_ADD:
	{
		TD op2 = result.top(); result.pop();
		TD op1 = result.top(); result.pop();
		return op1 + op2;
	}
	case OP_SUB:
	{
		TD op2 = result.top(); result.pop();
		TD op1 = result.top(); result.pop();
		return op1 - op2;
	}
	case OP_MUL:
	{
		TD op2 = result.top(); result.pop();
		TD op1 = result.top(); result.pop();
		return op1 * op2;
	}
	case OP_DIV:
	{
		TD op2 = result.top(); result.pop();
		TD op1 = result.top(); result.pop();
		return op2 ? (op1 / op2) : static_cast<TD>(1);
	}
	case OP_NEG:
	{
		TD op1 = result.top(); result.pop();
		return -op1;
	}
	case OP_VAR:
		return variables[*cCaseIdx][0];
	case OP_CONST:
		return static_cast<TD>(1.0);
	default:
		if (parameters[id - OP_PARAMETER].isVariable)
		{
			return variables[*cCaseIdx][parameters[id - OP_PARAMETER].index];
		}
		else
		{
			return constants[parameters[id - OP_PARAMETER].index];
		}
	}
}

// ------------------------------------------------------------------------------------------

CogLogicalProcessor::CogLogicalProcessor()
: maxVariables(0)
{
	validOps[OP_VAR] = CogOpInfo("var", 0);
	validOps[OP_AND] = CogOpInfo("and", 50);
	validOps[OP_OR] = CogOpInfo("or", 30);
	validOps[OP_NAND] = CogOpInfo("nand", 50);
	validOps[OP_NOR] = CogOpInfo("nor", 30);
	validOps[OP_XOR] = CogOpInfo("xor", 40);
	validOps[OP_NOT] = CogOpInfo("not", 300);
	validOps[OP_IF] = CogOpInfo("if", 300);
}

bool CogLogicalProcessor::readParameter(size_t opId, const string& value)
{
	int ivalue = atoi(value.c_str());
	if ((ivalue < 0) || (static_cast<size_t>(ivalue) >= maxVariables))
	{
		return false;
	}
	else if (!ivalue)
	{
		if (value[0] != '0')
		{
			return false;
		}
	}
	parameters.push_back(static_cast<size_t>(ivalue));
	return true;
}

inline
bool CogLogicalProcessor::processOperator(size_t id, stack<bool>& result)
{
	switch (id)
	{
	case OP_AND:
	{
		bool op2 = result.top(); result.pop();
		bool op1 = result.top(); result.pop();
		return op1 && op2;
	}
	case OP_OR:
	{
		bool op2 = result.top(); result.pop();
		bool op1 = result.top(); result.pop();
		return op1 || op2;
	}
	case OP_NAND:
	{
		bool op2 = result.top(); result.pop();
		bool op1 = result.top(); result.pop();
		return !(op1 && op2);
	}
	case OP_NOR:
	{
		bool op2 = result.top(); result.pop();
		bool op1 = result.top(); result.pop();
		return !(op1 || op2);
	}
	case OP_XOR:
	{
		bool op2 = result.top(); result.pop();
		bool op1 = result.top(); result.pop();
		return (op1 || op2) && !(op1 && op2);
	}
	case OP_NOT:
	{
		bool op1 = result.top(); result.pop();
		return !op1;
	}
	case OP_IF:
	{
		bool op3 = result.top(); result.pop();
		bool op2 = result.top(); result.pop();
		bool op1 = result.top(); result.pop();
		return op1 ? op2 : op3;
	}
	case OP_VAR:
		return variables[*cCaseIdx][0];
	default:
		return variables[*cCaseIdx][parameters[id - OP_PARAMETER]];
	}
}

// ------------------------------------------------------------------------------------------
